package com.fxu.framework.core.sql;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import com.fxu.framework.core.util.FieldUtil;
import lombok.Getter;
import lombok.ToString;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;

/**
 * 所有查询条件集中类
 * <p> 泛型: T ,表示Entity实体类 </p>
 *
 * @author 方旭
 * @version 1.0.0
 */
@Getter
@ToString
public class SQuery<SE extends SEntity<?>> implements SCompare<SQuery<SE>, SE>,  SOrder<SQuery<SE>, SE> {
    /**
     * 实体类的类
     */
    protected Class<SE> clazz;

    /**
     * 数据库表唯一主键
     */
    protected String priKey;

    /**
     * 实体类的表名
     */
    protected String table;

    /**
     * 是否显示大字段[text,blob等]
     */
    private boolean showText = false;

    /**
     * 是否只显示[Pri key : id]
     */
    private boolean onlyShowId = false;

    /**
     * 是否显示默认字段[createBy,updateBy,updateTime,version] 这些字段不显示
     */
    private boolean showDefault = false;

    /**
     * 不需要order by, countBy, sumBy, groupSumBy需要用到
     */
    private boolean notOrderBy = false;

    /**
     * 如果有order_no字段
     * null : order_no 无效
     * ASC : order by order_no ASC
     * DESC : order by order_no desc
     */
    private OrderNo orderNo = null;

    /**
     * sum(XXX)字段, sumBy, groupSumBy用到
     */
    private final List<SField<?>> sumFields = new ArrayList<>();

    /**
     * 分组(group by)字段集合
     */
    private final List<SField<?>> grpFields = new ArrayList<>();

    /**
     * 查询条件(会判断是否存在)
     */
    private final List<SValue> queryList = new ArrayList<>();

    /**
     * OR 查询条件(相同Key下的List必须至少两个)
     */
    private final List<SChild<SE>> andOrList = new ArrayList<>();

    /**
     * 复杂SQL组合条件集合
     */
    private final List<SApply<?, SE>> applyList = new ArrayList<>();

    /**
     * 显示字段集合
     */
    protected final List<SField<?>> showFields = new ArrayList<>();

    /**
     * 排序字段 order by XXX asc/desc 集合
     */
    protected final List<SValue> orderByFields = new ArrayList<>();

    /**
     * 级联读取(关联表(主类的字段)和表的哪些字段)
     */
    private Map<SField<SE>, List<SField<?>>> cascadeMap = new ConcurrentHashMap<>();

    /**
     * 创建SQuery对象
     * @param <SE> SEntity对象类型
     * @param clazz SEntity类型对象
     * @return SQuery 对象
     */
    public static <SE extends SEntity<?>> SQuery<SE> build(Class<SE> clazz) {
        return new SQuery<>(clazz);
    }

    /**
     * 构造方法,设置实体类
     * @param clazz 实体类
     */
    public SQuery(Class<SE> clazz) {
        this.clazz = clazz;
        this.priKey = FieldUtil.getPriKeyName(clazz);
    }

    /**
     * 构造方法,设置实体类
     * @param clazz 实体类
     * @param table 表名
     */
    public SQuery(Class<SE> clazz, String table) {
        this.clazz = clazz;
        this.table = table;
        this.priKey = FieldUtil.getPriKeyName(clazz);
    }

    /**
     * 构造方法,设置实体类
     */
    public SQuery() {
        // this((SE) null);
        Type type = getClass().getGenericSuperclass();
        if (type instanceof ParameterizedType) {
            this.clazz = (Class<SE>) ((ParameterizedType) type).getActualTypeArguments()[0];
            this.priKey = FieldUtil.getPriKeyName(clazz);
        } else {
            throw new RuntimeException("初始化错误!要么new SXxx<T>(){}，要么new SXxx<>(Class)");
        }
    }

    /**
     * 设置表名
     * @param table 表名
     * @return SUpdate 对象
     */
    public SQuery<SE> table(String table) {
        if (StrUtil.isNotBlank(table)) {
            this.table = table;
        }
        return this;
    }

    /**
     * 设置查询ID
     * @param id id
     * @return 返回本对象
     */
    public SQuery<SE> id(Serializable id) {
        if (id != null) {
            this.where(SType.EQ, new SField<SE>(clazz, priKey), id);
        }
        return this;
    }

    /**
     * 设置查询ID集合
     * @param ids id集合
     * @return SQuery 对象
     */
    public SQuery<SE> ids(Collection<? extends Serializable> ids) {
        // null则没有条件,如果是空,则1=2
        this.where(SType.IN, new SField<SE>(clazz, priKey), ids);
        return this;
    }

    /**
     * 设置查询没有任何order by, 被countBy, sumBy, grpSumBy用
     * @return 返回本对象
     */
    public SQuery<SE> notOrderBy() {
        this.notOrderBy = true;
        return this;
    }

    /**
     * 设置只查询id字段，被ApiIServiceImpl中的级联保存读取关联数据比较
     * @return 返回本对象
     */
    public SQuery<SE> onlyShowId() {
        onlyShowId = true;
        return this;
    }

    /**
     * 设置级联读取
     * @param field 级联字段对应的对象
     * @param sCascade 级联字段对象读取字段
     * @param <S> Entity对象约束
     * @return 返回本对象
     */
    public <S extends SEntity<?>> SQuery<SE> cascade(SFunc<SE, ?> field, SCascade<S> sCascade) {
        if (field != null && sCascade != null) {
            cascadeMap.put(new SField<>(clazz, field), sCascade.getShowFields());
        }
        return this;
    }

    /**
     * 设置级联读取Map对象
     * @param cascadeMap 级联读取Map对象，包含级联字段对象和级联对象读取字段列表
     * @return 返回本对象
     */
    public SQuery<SE> cascade(Map<SField<SE>, List<SField<?>>> cascadeMap) {
        if (MapUtil.isNotEmpty(cascadeMap)) {
            this.cascadeMap = cascadeMap;
        }
        return this;
    }

    /**
     * 或者 OR
     * select * from test 1=1 and (name = 'A' OR id = 1)
     * @param consumer 执行方法
     * @return 返回本对象
     */
    public SQuery<SE> andOr(Consumer<SChild<SE>> consumer) {
        SChild<SE> sChild = new SChild<>(this.clazz);
        consumer.accept(sChild);
        andOrList.add(sChild);
        return this;
    }

    /**
     * 或者 OR
     * select * from test where 1=1 and (name = 'A' OR id = 1)
     * @param orParams or参数集合
     * @return 返回本对象
     */
    public final SQuery<SE> andOr(List<SValue> orParams) {
        if (CollUtil.isNotEmpty(orParams)) {
            SChild<SE> sChild = new SChild<>(this.clazz);
            sChild.where(orParams);
            andOrList.add(sChild);
        }
        return this;
    }

    /**
     * in 子SQL
     * select * from student where class_id in (select id from clazz where name = '1')
     * @param <LE> 其他类约束
     * @param clazz 其他实体类
     * @param thisField 本实体字段
     * @param otherField 其他实体字段
     * @param consumer 执行方法
     * @return 返回本对象
     */
    public <LE extends SEntity<?>> SQuery<SE> inSql(Class<LE> clazz, SFunc<SE, ?> thisField, SFunc<LE, ?> otherField,
                                                    Consumer<SApply<LE, SE>> consumer) {
        return join(JoinType.INSQL, clazz, thisField, otherField, consumer);
    }

    /**
     * not in 子SQL
     * select * from student where class_id not in (select id from clazz where name = '1')
     * @param <LE> 其他类约束
     * @param clazz 其他实体类
     * @param thisField 本实体字段
     * @param otherField 其他实体字段
     * @param consumer 执行方法
     * @return 返回本对象
     */
    public <LE extends SEntity<?>> SQuery<SE> notInSql(Class<LE> clazz, SFunc<SE, ?> thisField, SFunc<LE, ?> otherField,
                                                       Consumer<SApply<LE, SE>> consumer) {
        return join(JoinType.NOTINSQL, clazz, thisField, otherField, consumer);
    }

    /**
     * join 包含了 left join, right join, inner join
     * select * from student left join grade where student.id = 1 and grade.level = '1'
     * @param <LE> 其他类约束
     * @param joinType 连接类型
     * @param clazz 其他实体类
     * @param thisField 本实体字段
     * @param otherField 其他实体类字段
     * @return 返回本对象
     */
    public <LE extends SEntity<?>> SQuery<SE> join(JoinType joinType, Class<LE> clazz, SFunc<SE, ?> thisField,
                                                   SFunc<LE, ?> otherField) {
        return join(joinType, clazz, thisField, otherField, null);
    }

    /**
     * join 包含了 left join, right join, inner join
     * select * from student left join grade where student.id = 1 and grade.level = '1'
     * @param <LE> 其他类约束
     * @param joinType 连接类型
     * @param clazz 其他实体类
     * @param thisField 本实体字段
     * @param otherField 其他实体类字段
     * @param consumer 执行方法
     * @return 返回本对象
     */
    public <LE extends SEntity<?>> SQuery<SE> join(JoinType joinType, Class<LE> clazz, SFunc<SE, ?> thisField,
                                                   SFunc<LE, ?> otherField, Consumer<SApply<LE, SE>> consumer) {
        SApply<LE, SE> sApply = new SApply<>(clazz);
        sApply.setJoinType(joinType);
        sApply.setThisField(new SField<>(clazz, otherField));
        sApply.setOtherField(new SField<>(getClazz(), thisField));
        if (consumer != null) {
             consumer.accept(sApply);
        }
        applyList.add(sApply);
        return this;
    }

    /**
     * 设置多个读取显示字段
     * @param fields 显示字段集合
     * @return 本类对象
     */
    public SQuery<SE> show(Collection<SField<SE>> fields) {
        if (fields != null && fields.size() > 0) {
            getShowFields().addAll(fields);
        }
        return this;
    }

    /**
     * 设置读取显示字段
     * @param sFunctions 显示字段集合
     * @return 本类对象
     */
    @SafeVarargs
    @SuppressWarnings("varargs")
    public final SQuery<SE> show(SFunc<SE, ?>... sFunctions) {
        if (sFunctions != null) {
            for (SFunc<SE, ?> field : sFunctions) {
                getShowFields().add(new SField<>(getClazz(), field));
            }
        }
        return this;
    }

    /**
     * 设置读取显示字段
     * @param sFunctions 显示字段集合
     * @return 本类对象
     */
    @SafeVarargs
    @SuppressWarnings("varargs")
    public final SQuery<SE> select(SFunc<SE, ?>... sFunctions) {
        if (sFunctions != null) {
            for (SFunc<SE, ?> field : sFunctions) {
                getShowFields().add(new SField<>(getClazz(), field));
            }
        }
        return this;
    }

    /**
     * 设置查询条件
     * @param type 查询条件类型
     * @param field 查询条件字段
     * @param value 查询条件值
     * @return 返回本对象
     */
    @Override
    public SQuery<SE> where(SType type, String field, Object value) {
        Class<SE> clazz = getClazz();
        if (type != null && clazz != null && field != null && value != null) {
            queryList.add(new SValue(type, new SField<>(clazz, field), value));
        }
        return this;
    }

    /**
     * 统计字段 sumBy, grpSumBy 使用字段 : sum(XX), sum(YY)
     * @param fields 对应字段集合 [XX, YY]
     * @return 返回本对象
     */
    @SafeVarargs
    @SuppressWarnings("varargs")
    public final SQuery<SE> sumFields(SFunc<SE, ?> ... fields) {
        if (fields != null) {
            for (SFunc<SE, ?> field : fields) {
                getSumFields().add(new SField<>(getClazz(), field));
            }
        }
        return this;
    }

    /**
     * 设置分组字段 : group by XX, YY
     * @param fields 对应字段集合 [XX, YY]
     * @return 返回本对象
     */
    @SafeVarargs
    @SuppressWarnings("varargs")
    public final SQuery<SE> groupBy(SFunc<SE, ?> ... fields) {
        if (fields != null) {
            for (SFunc<SE, ?> field : fields) {
                getGrpFields().add(new SField<>(getClazz(), field));
            }
        }
        return this;
    }

    /**
     * 是否显示大字段, text, blob等
     * @param showText 是否显示
     * @return 返回本对象
     */
    public SQuery<SE> showText(boolean showText) {
        this.showText = showText;
        return this;
    }

    /**
     * 是否显示默认字段[createBy,updateBy,updateTime,status,version] 这些字段不显示
     * @return 返回本对象
     */
    public SQuery<SE> showDefault() {
        this.showDefault = true;
        return this;
    }

    /**
     * 设置查询没有任何order by, 被countBy, sumBy, grpSumBy用
     * @param orderNo 设置的排序对象
     * @return 返回本对象
     */
    public SQuery<SE> orderNo(OrderNo orderNo) {
        this.orderNo = orderNo;
        return this;
    }

    /**
     * 用于ID字段的处理(只是标识, ApiIServiceImpl中会判断)
     * @param <T> 入参类型
     * @param arg 入参
     * @return 入参
     */
    public static <T> T idFun(T arg) {
        return arg;
    }
}
